﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.Configuration;

using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
using Lextm.SharpSnmpLib.Security;

namespace GCPUD.Autoprov.Network
{
    public class Node
    {
        //Constants
        const string sysOID = ".1.3.6.1.2.1.1.2.0";
        const string vtpVlanStateOID = ".1.3.6.1.4.1.9.9.46.1.3.1.1.2";
        const string dot1dTpFdbAddressOID = ".1.3.6.1.2.1.17.4.3.1.1";
        const string dot1dTpFdbPortOID = ".1.3.6.1.2.1.17.4.3.1.2";
        const string dot1dBasePortIfIndexOID = ".1.3.6.1.2.1.17.1.4.1.2";
        const string ifIndexOID = ".1.3.6.1.2.1.2.2.1.1";
        const string ifOperStatusOID = ".1.3.6.1.2.1.2.2.1.8";
        const string ifAliasOID = ".1.3.6.1.2.1.31.1.1.1.18";
        const string ifSpeedOID = "1.3.6.1.2.1.2.2.1.5";
        const string ifNameOID = ".1.3.6.1.2.1.31.1.1.1.1";
        const string ifTypeOID = "1.3.6.1.2.1.2.2.1.3";
        const string cdpIpOID = ".1.3.6.1.4.1.9.9.23.1.2.1.1.4";
        const string pagpGroupIfIndexOID = ".1.3.6.1.4.1.9.9.98.1.1.1.1.8";
        const string ifNumberOID = "1.3.6.1.2.1.2.1.0";

        /*
        //Private Variables
        private string _community = "GrANt323";
        private IPAddress _nodeIpAddress;
        private string _nodeSysOid;
        private string _ifNumber;
        private List<Port> _ports;
        private MacTable _macTable = new MacTable();
        private List<string> _vlans = new List<string>();

        /// <summary>
        /// Public Accessors
        /// </summary>
        public string switchID { get; set; }
        public int idnode { get; set; } // this is for the database
        public string sysName { get; set; }
        public List<Port> Ports
        {
            get { return _ports; }
            set { _ports = value; }
        }
        //public void getPorts()
        //{
        //    Data data = new Data();
        //    Ports = data.getPortData(idnode);
        //}
        public List<string> vlans
        {
            get { return _vlans; }
            set { _vlans = value; }
        }
        public string NodeSysOid
        {
            get { return _nodeSysOid; }
            set { _nodeSysOid = value; }
        }
        public string IfNumber
        {
            get { return _ifNumber; }
            set { _ifNumber = value; }
        }
        /// <summary>
        /// Constructor no arguments.... this is fail
        /// </summary>
        public Node()
        {
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <param name="comm"></param>
        public Node(IPAddress ipAddress, string comm)
        {
            _nodeIpAddress = ipAddress;
            _community = comm;
            _ports = new List<Port>();
            try
            {
                NodeSysOid = GetSwitchSysOid(_nodeIpAddress, _community);
                IfNumber = GetifNumber(_nodeIpAddress, _community);
                _ports = GetPorts(_nodeIpAddress, _community);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: " + e.Message);

            }
            //GetVlanMacTable();
        }
        /// <summary>
        /// Constructor used by Data
        /// </summary>
        /// <param name="_sysname"></param>
        public Node(string _sysname)
        {
            sysName = _sysname;
        }
        /// <summary>
        /// Constructor used by Data
        /// </summary>
        /// <param name="_idnode"></param>
        /// <param name="_sysname"></param>
        public Node(int _idnode, string _sysname)
        {
            sysName = _sysname;
            idnode = _idnode;
        }
        /// <summary>
        /// Returns a List of Vlans active on the switch
        /// </summary>
        /// <returns></returns>
        /// 

        */
        public static List<Port> GetPorts(IPAddress ipAddress, string community)
        {
            if (ipAddress == null || community == null) return null;
            List<Port> returnPortList = new List<Port>();
            List<Variable> resultIfIndex = Snmp.Walk(ifIndexOID, ipAddress, community);
            if (resultIfIndex != null || resultIfIndex.Count > 0)
            {
                foreach (Variable var in resultIfIndex)
                {
                    string ifIndexSNMP = var.Data.ToString();
                    string TypeOID = ifTypeOID + "." + ifIndexSNMP;
                    string ifTypeSNMP = Snmp.Get(TypeOID, ipAddress, community).ElementAt(0).ToString();
                    if (ifTypeSNMP.Equals("6")) // 6 is Ethernet type
                    {
                        string speedOID = ifSpeedOID + "." + ifIndexSNMP;
                        string NameOID = ifNameOID + "." + ifIndexSNMP;
                        string OperStatusOID = ifOperStatusOID + "." + ifIndexSNMP;
                        string ifSpeedSNMP = Snmp.Get(speedOID, ipAddress, community).ElementAt(0).ToString();
                        string ifNameSNMP = Snmp.Get(NameOID, ipAddress, community).ElementAt(0).ToString();
                        string ifOperStatus = Snmp.Get(OperStatusOID, ipAddress, community).ElementAt(0).ToString();

                        returnPortList.Add(new Port { ifIndex = ifIndexSNMP, PortSpeed = ifSpeedSNMP, IfOperStatus = ifOperStatus, ifName = ifNameSNMP, ifType = ifTypeSNMP });
                    }
                }
            }
            else return null;
            if (returnPortList.Count > 0)
            {
                return returnPortList;
            }
            else return null;
            

        }
        /// <summary>
        /// Sets the Number of Interfaces on the Node
        /// </summary>
        public static string GetifNumber(IPAddress ipAddress, string community)
        {
            if (ipAddress == null || community == null)
            {
                return null;
            }
            List<ISnmpData> result = Snmp.Get(ifNumberOID, ipAddress, community);
            if (result[0] != null)
            {
                return result[0].ToString();

            }
            return null;
        }
        /// <summary>
        /// Gets the SystemOID from the Node
        /// </summary>
        public static string GetSwitchSysOid(IPAddress ipAddress, string community)
        {
            if (ipAddress == null || community == null) return null;
            List<ISnmpData> result = Snmp.Get(sysOID, ipAddress, community);
            if (result != null && result.Count > 0 && result[0] != null)
            {
                return result[0].ToString();

            }
            return null;
        }
        /// <summary>
        /// Gets the Active Vlans from the Node
        /// </summary>
        private static List<Vlan> GetActiveVlans(IPAddress ipAddress, string community)
        {
            if (ipAddress == null || community == null) return null;
            List<Vlan> returnVlanList = new List<Vlan>();
            List<Variable> result = new List<Variable>();
            try
            {
                result = Snmp.Walk(vtpVlanStateOID, ipAddress, community);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error GetActiveVlans(): " + e.Message);
                return null;
            }
            if (result == null) return null;
            List<string> returnvlans = new List<string>();
            foreach (Variable var in result)
            {
                string[] oid = var.Id.ToString().Split('.');
                string vlan = oid[oid.Length - 1];
                //Debug.WriteLine(vlan);
                returnVlanList.Add(new Vlan { VlanNum = vlan });
            }
            return returnVlanList;

        }
        /// <summary>
        /// Returns a List of Mac Table Entries
        /// </summary>
        /// <param name="macAddress"></param>
        /// <returns></returns>
        public static List<MacTableEntry> GetMacEntries(Mac macAddress, IPAddress nodeIpAddress, string community)
        {
            if (macAddress == null || nodeIpAddress == null || community == null) return null;
            MacTable _macTable = GetVlanMacTable(nodeIpAddress, community);
            //Retrieve the table from the switch

            //Returns a list of matching addresses from the table
            return _macTable.Matches(macAddress);
        }
        /// <summary>
        /// Stores all vlan/mac addresses from the switch
        /// </summary>

        public static Mac ConvertSnmpDataToMac(ISnmpData data)
        {
            
            byte[] tempbyte = data.ToBytes();

            string p0 = tempbyte[2].ToString("X2");
            string p1 = tempbyte[3].ToString("X2");
            string p2 = tempbyte[4].ToString("X2");
            string p3 = tempbyte[5].ToString("X2");
            string p4 = tempbyte[6].ToString("X2");
            string p5 = tempbyte[7].ToString("X2");


            
            string mac_string = string.Format("{0}:{1}:{2}:{3}:{4}:{5}", p0, p1, p2, p3, p4, p5);
            //Console.WriteLine(mac_string);
            return new Mac(mac_string);
        }
        public static MacTable GetVlanMacTable(IPAddress _nodeIpAddress, string _community)
        {
            if (_nodeIpAddress == null || _community == null) return null;
            MacTable _macTable = new MacTable();
            List<Vlan> vlans = GetActiveVlans(_nodeIpAddress, _community);
            if (vlans == null) return null;
            _macTable = GetVlanMacTable(vlans, _nodeIpAddress, _community);
            if (_macTable == null) return null;
            return _macTable;

        }
        /// <summary>
        /// Stores only the vlan/mac address entries associated with the provided mac address
        /// </summary>
        /// <param name="macAddress"></param>
        public static MacTable GetVlanMacTable(List<Vlan> _vlans, IPAddress _nodeIpAddress, string _community)
        {
            MacTable _macTable = new MacTable();
            try
            {
                foreach (Vlan vlan in _vlans)
                {
                    MacTable vlanMacTable = new MacTable();
                    vlanMacTable = GetVlanMacTable(vlan, _nodeIpAddress, _community);
                    if (vlanMacTable == null) continue;
                    foreach (MacTableEntry macTableEntry in vlanMacTable)
                    {
                        _macTable.Add(macTableEntry);
                    }
                    
                }
                return _macTable;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        public static MacTable GetVlanMacTable(Vlan vlan, IPAddress _nodeIpAddress, string _community)
        {
            MacTable _macTable = new MacTable();
            try
            {
                int _vlan = int.Parse(vlan.VlanNum);
                if (_vlan < 1002 || _vlan > 1005)
                {
                    //For specific MAC address
                    string oid = dot1dTpFdbAddressOID;

                    List<Variable> result = Snmp.BulkWalk(oid, _nodeIpAddress, _community + "@" + vlan.VlanNum);
                    if (result == null) return null;
                    foreach (Variable variable in result)
                    {
                        MacTableEntry entry = new MacTableEntry();
                        ISnmpData snmpData = variable.Data;

                        Mac mac = ConvertSnmpDataToMac(snmpData);

                        entry.macAddress = mac;

                        try
                        {
                            //Console.WriteLine("MAC: " + mac.ToString());
                            string bridgeport = GetBridgePort(mac, vlan, _nodeIpAddress, _community);
                            if (!bridgeport.Equals("0"))
                            {
                                entry.ifIndex = GetInterfaceifIndex(bridgeport, vlan, _nodeIpAddress, _community);
                                if (entry.ifIndex == null) continue;
                                string interfacename = GetInterfaceName(bridgeport, vlan, _nodeIpAddress, _community);
                                if (interfacename == null) continue;
                                if (interfacename.Equals("NoSuchInstance")) continue;
                                string interfacedesc = GetInterfaceAlias(bridgeport, vlan, _nodeIpAddress, _community);
                                if (interfacedesc == null) continue;
                                entry.Interface = interfacename;
                                entry.Description = interfacedesc;
                                entry.Vlan = vlan;
                                _macTable.Add(entry);
                            }
                        }
                        catch 
                        {

                            continue;
                            //Console.WriteLine(ex);
                        }


                    }
                }
                return _macTable;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }

        }
        /// <summary>
        /// Returnes the Bridge port assocated with the provided mac address and vlan
        /// </summary>
        /// <param name="macaddress"></param>
        /// <param name="vlan"></param>
        /// <returns></returns>
        public static string GetBridgePort(Mac macaddress, Vlan vlan, IPAddress _nodeIpAddress, string _community)
        {
            bool stillTrying = true;
            int tryTimes = 3;
            string oid = dot1dTpFdbPortOID + "." + macaddress.ToOid();
            while (stillTrying)
            {
                if (tryTimes == 0)
                {
                    stillTrying = false;
                }
                try
                {
                    
                    //Console.WriteLine("OID: " + oid);
                    List<ISnmpData> tempdata = Snmp.Get(oid, _nodeIpAddress, _community + "@" + vlan.VlanNum);
                    if (tempdata == null)
                    {
                        tryTimes = 0;
                        continue;
                    }
                    string bridgeport = tempdata.ElementAt(0).ToString();
                    //Console.WriteLine("BridgePort: " + bridgeport);
                    return bridgeport;
                }
                catch 
                {
                    tryTimes -= 1;

                    //if (stillTrying)
                    //{
                    //    //Console.WriteLine(oid);
                    //    //Console.WriteLine("Tries left = " + tryTimes);
                    //}
                    //else throw new ApplicationException(e.Message);
                }
            }
            return null;
        }

        public static string GetInterfaceName(Mac mac, Vlan vlan, IPAddress hub, string community)
        {
            string bridgeport = GetBridgePort(mac, vlan, hub, community);
            if (bridgeport == null) return null;
            string interfacename = GetInterfaceName(bridgeport, vlan, hub, community);
            if (interfacename == null) return null;
            return interfacename;
        }
        /// <summary>
        /// Returns the Interface Name associated with the provided bridge port and vlan
        /// </summary>
        /// <param name="bridgeport"></param>
        /// <param name="vlan"></param>
        /// <returns>Interface Name for supplied bridgeport and vlan</returns>
        public static string GetInterfaceName(string bridgeport, Vlan vlan, IPAddress _nodeIpAddress, string _community)
        {
            try
            {
                string oid = dot1dBasePortIfIndexOID + "." + bridgeport;
                List<ISnmpData> tempdata1 = Snmp.Get(oid, _nodeIpAddress, _community + "@" + vlan.VlanNum);
                if (tempdata1 == null) return null;
                string baseport = tempdata1.ElementAt(0).ToString();
                oid = ifNameOID + "." + baseport;
                List<ISnmpData> tempdata2 = Snmp.Get(oid, _nodeIpAddress, _community);
                if (tempdata2 == null) return null;
                return tempdata2.ElementAt(0).ToString();
            }
            catch
            {
                return null;
            }
        }
        /// <summary>
        /// returns the SNMP ifAlias
        /// </summary>
        /// <param name="bridgeport"></param>
        /// <param name="vlan"></param>
        /// <returns></returns>
        public static string GetInterfaceAlias(string bridgeport, Vlan vlan, IPAddress _nodeIpAddress, string _community)
        {
            try
            {
                string oid = dot1dBasePortIfIndexOID + "." + bridgeport;
                List<ISnmpData> tempdata1 = Snmp.Get(oid, _nodeIpAddress, _community + "@" + vlan.VlanNum);
                if (tempdata1 == null) return null;
                string baseport = tempdata1.ElementAt(0).ToString();
                oid = ifAliasOID + "." + baseport;
                List<ISnmpData> tempdata2 = Snmp.Get(oid, _nodeIpAddress, _community);
                if (tempdata2 == null) return null;
                return tempdata2.ElementAt(0).ToString();
            }
            catch
            {
                return null;
            }
        }
        /// <summary>
        /// Returns the ifIndex of the provided bridgeport and vlan
        /// </summary>
        /// <param name="bridgeport"></param>
        /// <param name="vlan"></param>
        /// <returns></returns>
        public static string GetInterfaceifIndex(string bridgeport, Vlan vlan, IPAddress _nodeIpAddress, string _community)
        {
            try
            {
                string oid = dot1dBasePortIfIndexOID + "." + bridgeport;
                List<ISnmpData> tempdata1 = Snmp.Get(oid, _nodeIpAddress, _community + "@" + vlan.VlanNum);
                string baseport = tempdata1.ElementAt(0).ToString();
                oid = ifIndexOID + "." + baseport;
                List<ISnmpData> tempdata2 = new List<ISnmpData>();
                tempdata2 = Snmp.Get(oid, _nodeIpAddress, _community);

                return tempdata2.ElementAt(0).ToString();
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        /// <summary>
        /// Returns the IP Address of a neighboring CDP supporting switch if one exists on the provided ifIndex
        /// </summary>
        /// <param name="ifIndex"></param>
        /// <returns></returns>
        public static string GetCDPNeighbor(string ifIndex, IPAddress _nodeIpAddress, string _community)
        {
            try
            {
                string oid = cdpIpOID + "." + ifIndex;
                List<Variable> result = Snmp.Walk(oid, _nodeIpAddress, _community);
                //List<ISnmpData> tempdata2 = SnmpGet(oid, switchIpAddress, community);
                //List<ISnmpData> data = SnmpGet(oid, switchIpAddress, community);
                string[] ipArray = { "0", "0", "0", "0" };
                if (result.Count > 0)
                {

                    byte[] hexIP = result.ElementAt(0).Data.ToBytes();


                    for (int i = 2; i < hexIP.Count(); i++)
                    {
                        ipArray[i - 2] = Convert.ToInt32(hexIP[i]).ToString();
                    }
                }
                string ip = ipArray[0] + "." + ipArray[1] + "." + ipArray[2] + "." + ipArray[3];

                return ip;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        public static List<MacTableEntry> GetInterfaceMacForVlan(Vlan vlan, Mac gatewaymac, IPAddress _nodeIpAddress, string _community)
        {
            List<Vlan> vlans = new List<Vlan>();
            vlans.Add(vlan);
            MacTable macTable = new MacTable();
            macTable = GetVlanMacTable(vlan, _nodeIpAddress, _community);
            string gatewayBridgeport = GetBridgePort(gatewaymac, new Vlan { VlanNum = "127" }, _nodeIpAddress, _community);
            string gatewayifIndex = GetInterfaceifIndex(gatewayBridgeport, new Vlan { VlanNum = "127" }, _nodeIpAddress, _community);
            List<MacTableEntry> macTableList = new List<MacTableEntry>();
            foreach (MacTableEntry macTableEntry in macTable)
            {
                if (macTableEntry.Vlan.VlanNum == vlan.VlanNum && macTableEntry.ifIndex == gatewayifIndex)
                {
                    macTableList.Add(macTableEntry);
                }
            }
            return macTableList;
        }
        //delegates
        public delegate void IfIndexDelegate();
        public List<int> GetIfIndex(IfIndexDelegate gatewayHandler)
        {
            List<int> indexTable = new List<int>();
            if (gatewayHandler != null)
            {
                gatewayHandler();
            }

            return indexTable;
        }
    }
}
